package metrics

import (
	"fmt"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/load"
	"github.com/shirou/gopsutil/mem"
	"net/http"
	"runtime"
	"sync"
	"time"
)

//混合方式数据结构
type statsMetrics []struct {
	desc    *prometheus.Desc
	eval    func(*mem.VirtualMemoryStat) float64
	valType prometheus.ValueType
}

type Exporter struct {
	mux          *http.ServeMux
	mu           *sync.RWMutex
	requestCount int64

	goroutinesDesc *prometheus.Desc //协程监控
	requestDesc    *prometheus.Desc //请求次数监控
	requestSummary *prometheus.Desc //请求相应时间监控
	memoryDesc     statsMetrics     //内存监控
	cpuRateDesc    *prometheus.Desc //cpu使用率监控
	cpuLoad1Desc   *prometheus.Desc //cpu负载监控
	cpuLoad5Desc   *prometheus.Desc //cpu负载监控
	cpuLoad15Desc  *prometheus.Desc //cpu负载监控
	//diskIODesc  *prometheus.Desc //磁盘io状态
	//diskRateDesc  *prometheus.Desc //磁盘io状态
}

func NewExporter() *Exporter {
	mux := http.NewServeMux()
	e := &Exporter{
		mux:            mux,
		mu:             &sync.RWMutex{},
		requestCount:   0,
		goroutinesDesc: prometheus.NewDesc("goroutine_count", "协程数量", nil, nil),
		requestDesc:    prometheus.NewDesc("request_count", "请求数量", nil, nil),
		requestSummary: prometheus.NewDesc("request_summary", "请求数量", nil, nil),
		memoryDesc: statsMetrics{
			{
				desc: prometheus.NewDesc(
					"total_mem",
					"内存总量",
					nil, nil),
				valType: prometheus.GaugeValue,
				eval:    func(ms *mem.VirtualMemoryStat) float64 { return float64(ms.Total) / 1e9 },
			},
			{
				desc: prometheus.NewDesc(
					"free_mem",
					"内存空闲",
					nil, nil),
				valType: prometheus.GaugeValue,
				eval:    func(ms *mem.VirtualMemoryStat) float64 { return float64(ms.Free) / 1e9 },
			},
		},
		cpuRateDesc:   prometheus.NewDesc("cpu_rate", "cpu使用率", nil, nil),
		cpuLoad1Desc:  prometheus.NewDesc("cpu_load1", "cpu1分钟负载", nil, nil),
		cpuLoad5Desc:  prometheus.NewDesc("cpu_load5", "cpu5分钟负载", nil, nil),
		cpuLoad15Desc: prometheus.NewDesc("cpu_load15", "cpu15分钟负载", nil, nil),
		//diskIODesc:prometheus.NewDesc("disk_io","磁盘io", nil,nil),
		//diskRateDesc:prometheus.NewDesc("disk_rate","磁盘使用率", nil,nil),
	}

	return e
}

func (n *Exporter) Describe(ch chan<- *prometheus.Desc) {
	ch <- n.goroutinesDesc
	ch <- n.requestDesc
	ch <- n.requestSummary
	for _, metric := range n.memoryDesc {
		ch <- metric.desc
	}
	ch <- n.cpuRateDesc
	ch <- n.cpuLoad1Desc
	ch <- n.cpuLoad5Desc
	ch <- n.cpuLoad15Desc
	//ch <- n.diskIODesc
	//ch <- n.diskRateDesc
}

func (n *Exporter) Collect(ch chan<- prometheus.Metric) {
	n.mu.RLock()
	defer n.mu.RUnlock()
	ch <- prometheus.MustNewConstMetric(n.goroutinesDesc, prometheus.GaugeValue, float64(runtime.NumGoroutine()))
	ch <- prometheus.MustNewConstMetric(n.requestDesc, prometheus.GaugeValue, float64(n.requestCount))
	//request后期想办法修改
	ch <- prometheus.MustNewConstMetric(n.requestSummary, prometheus.GaugeValue, float64(n.requestCount))
	//内存使用
	vm, err := mem.VirtualMemory()
	if err != nil {
		fmt.Println(err)
		return
	}
	for _, metric := range n.memoryDesc {
		ch <- prometheus.MustNewConstMetric(metric.desc, metric.valType, metric.eval(vm))
	}
	//cpu使用率
	cpuRate, err := cpu.Percent(time.Second*2, false)
	if err != nil {
		fmt.Println(err)
		return
	}
	ch <- prometheus.MustNewConstMetric(n.cpuRateDesc, prometheus.GaugeValue, cpuRate[0])

	//cou负载
	info, err := load.Avg()
	if err != nil {
		fmt.Println(err)
		return
	}
	ch <- prometheus.MustNewConstMetric(n.cpuLoad1Desc, prometheus.GaugeValue, info.Load1)
	ch <- prometheus.MustNewConstMetric(n.cpuLoad5Desc, prometheus.GaugeValue, info.Load5)
	ch <- prometheus.MustNewConstMetric(n.cpuLoad15Desc, prometheus.GaugeValue, info.Load15)
}

func (n *Exporter) IncReq() {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.requestCount++
}

func (n *Exporter) DecReq() {
	n.mu.Lock()
	defer n.mu.Unlock()
	n.requestCount--
}
